Skip to content

vNext: Support Gree zone controllers - #552

Open
Twintop wants to merge 12 commits into
RobHofmann:5.0-devfrom
Twintop:zone-controller-support
Open

Twintop wants to merge 12 commits into
RobHofmann:5.0-devfrom
Twintop:zone-controller-support

Conversation

@Twintop

@Twintop Twintop commented Oct 10, 2026 •

Copy link
Copy Markdown

Context

This adds support for Gree zone controllers. It's part of porting the changes I made for my own zone controller (Gree LE60-13/GH) over to this project, and #547, #548 and #549 got things ready for it. It also picks up the DeviceType idea @p-monteiro suggested in #549. Two more PRs build on this one: a per mode memory for the zone target temperatures, and the same memory for every device. Background and the full plan: https://github.com/Twintop/ha-gree-zone-controller/blob/main/upstream/README.md

What a zone controller is

A zone controller drives the dampers of one ducted unit. Seen: LE60-13/GH with a ME31-00/C13 WiFi module, firmware 362001062617+U-W05SAV1.27.bin (the Braemar LE60 uses the same module). It answers the scan like a VRF gateway, with subCnt above zero, but its sub-units aren't indoor units:

  • Sub-unit 00 is the ducted unit. It answers Pow, Mod, WdSpd, AllErr and TemUnit, and StTem and StFahFlg always as 0. Its Pow is the power of the whole system.
  • Sub-units 01 to 08 are the zones. A zone answers Pow (the damper), StTem and StFahFlg (its target temperature), TemUnit, and a copy of Mod and WdSpd of the ducted unit.
  • No sub-unit answers SetTem, TemRec, TemUn or Add0.5.
  • Every sub-unit answers the info request with the name of the controller (GR-ZCntrlr_5000_02_..._EC), and the sub-device list calls every unit zone. The name and the 00 suffix of the ducted unit are fixed in the WiFi module firmware (checked v1.21, v1.27 and v1.28), not taken from the units.
  • StTem + 16 and StFahFlg work like SetTem and TemRec: in °C the bit is the half degree (the wall panel only shows whole degrees), in °F it's the same table. The unit is TemUnit (0 °C, 1 °F), not TemUn. Written to the ducted unit, the whole controller and the wall panel switch.

Symptoms on 5.0-dev

  • All sub-units are named zone, and the gateway device is labelled "VRF gateway".
  • The ducted unit numbers its modes 1 Cool, 2 Heat, 3 Dry, 4 Fan, 5 Auto. Read with the normal numbers, Fan shows as Heat, and Auto raises ValueError from OperationMode(5).
  • The ducted unit has no Tur column. Turbo is fan speed 6, so turbo is not offered, and fan speed 6 raises ValueError from FanSpeed(6).
  • Every zone gets a climate entity with the mode of the ducted unit, and no target temperature, because StTem is not polled.
  • Every fan change logs Property Quiet is unsupported on this device at error level. This happens on any unit without Quiet.
  • Not zone specific: after a device with a "connection failed" repair issue is removed, the next setup fails with RuntimeError: dictionary changed size during iteration, because cleanup_device_connection_issues() deletes from registry.issues while it loops over it.

Change

Protocol layer:

  • DeviceType for every device, set after the info fetch: ac_unit (a standalone unit), vrf_unit (an indoor unit behind a VRF gateway), zone_controller (the ducted unit of a zone controller) and zone. A sub-unit whose name starts with ZONE_CONTROLLER_NAME_PREFIX belongs to a zone contrC_UNIT_SUFFIX` tells the ducted unit from the zones.Nothing is added to the config entry, so there is no migration. The type is in the device diagnostics.
  • The columns come from what a device answers. StTemre in the default poll, and the first poll drops themon a unit that doesn't answer them, like any other column, so other units send the same columns as before after it. A device uses TemUnwhen it answers it andTemUnitonly withoutere the columns can't tell: the mode numbers and turbo of the zone controller, and which entities a sub-unit gets (the ducted unit answersStTem` too, always 0).
  • operation_mode and set_operation_mode() convertbers.
  • Turbo is fan speed 6 on the ducted unit: feature_turbo, set_feature_turbo() and supports_property(FEAT_TURBO_MODE) handle that.
    Leaving Cool or Heat drops turbo to High.
  • A zone uses target_temperature and set_target_temperature() like any other device, with the same SetTem/TemRec table in °C and
    °F. set_target_temperature() rounds °C to the nearee degrees. A zone write sends StTem and StFahFlgtogether, because the controller keeps the old bit when only StTem comes in.
  • Discovery names the sub-units AC unit and `Zone 1oller name and the MAC suffix.

Home Assistant layer:

  • A zone gets no climate entity. It gets a switch with the name of the zone, and a Target Temperature number that follows the
    controller's unit: 16 to 30 °C in half degrees, or 61ome Assistant shows it in your own unit.
  • The Temperature Units select shows up on the AC unit of a zone controller and switches the whole controller, wall panel included.
    Zones don't get their own.
  • The zone switch is the optional feature Zone Switch, so it can be left out for a zone that must always stay open. Turning such a
    zone off is refused by the controller or turns the whs only offered on zones, and zones do not show theclimate options.
  • GreeEntityDescription.device_filter limits an enthen the props alone can't tell.
  • The gateway device of a zone controller is labelled "Zone controller", from the type of a bound sub-unit. With no bound sub-unit, an
    existing device keeps its label and a new one gets thller_label()`).
  • Device options: when several sub-units are set up in one go, the next form only takes the mode lists and features of the one before,
    not its name, and never zone. A zone always starts the features default is filtered against what thedevice supports.
  • The climate entity only sets Quiet and Turbo on uni
  • Number entities pass their value to the device as it is, and the device rounds. The humidity number keeps its int().
  • cleanup_device_connection_issues() loops over a c
  • en.json, icons.json, docs/protocol.md, docs/entities.md, docs/configuration.md, docs/architecture.md and
    supported-devices.md are updated.

Testing

  • pytest: 280 passed (255 on 5.0-dev, plus 25 new).ake controller that behaves like the real one:subDev form only, no cid, per unit columns, mode, fan and unit mirrored from the ducted unit to the zones, and an empty result above
    the column limit.
  • Ruff 0.17.0 passes. Mypy (run with Home Assistant 2026.10 installed, using the repo's mypy.ini) and Pylint show nothing new compared
    with 5.0-dev.
  • Real device, LE60-13/GH on Home Assistant 2026.10:
    • Entities and states are right (no target temperatturbo offered). The gateway shows as "Zone controller" with the AC unit and zones linked. A zone target temperature, a zone switch, the fan speed and Heat with Turbo all reach the controller.
    • After the first poll, every sub-unit keeps only StTem, StFahFlg and TemUnit of the temperature columns.
    • Half degrees: a zone set to 18.5 °C goes out as `nd the controller keeps it.
    • °F: the Temperature Units select switches the controller and the wall panel to °F, and a zone set to 21 °C in Home Assistant goes out as 70 °F (StTem 5, StFahFlg 1). Values set on read back right.
    • An offline VRF gateway in the same entry no longer breaks setup, and removing it again cleans up its repair issue and its device.
    • No errors in the log.
  • probe_device_limits() measured 29 columns. The module answers 30 with an empty result, and requests far above that (about 40 columns and more) made it reboot.

A Gree zone controller (seen: LE60-13/GH with a ME31-00/C13 WiFi module)
drives the dampers of one ducted unit. It answers like a VRF gateway, but
its sub-units are the ducted unit (MAC suffix 00) and up to eight zones
(01 to 08), and they behave differently from VRF indoor units.

Protocol layer:
- GreeDevice detects the role of a sub-unit after the info fetch: it
  reports the name of the controller (GR-ZCntrlr...), and the MAC suffix
  tells the ducted unit from a zone.
- The ducted unit numbers its modes 1 Cool, 2 Heat, 3 Dry, 4 Fan, 5 Auto.
  Before this, Fan showed as Heat and Auto raised. operation_mode and
  set_operation_mode convert for every unit with a zone role.
- It has no Tur column. Turbo is fan speed 6, only in Cool and Heat.
- A zone has a target temperature, StTem, in degrees Celsius minus 16.
  Only zones poll it, so other units send the same columns as before.
- Discovery names the sub-units "AC unit" and "Zone 1" to "Zone 8"
  instead of "zone" for every one of them.

Home Assistant layer:
- A zone gets no climate entity. It gets a switch with the name of the
  zone and a Target Temperature number.
- The switch is the optional feature Zone Switch, so it can be left out
  for a zone that must always stay open. It is only offered on zones, and
  zones do not show the climate options.
- GreeEntityDescription.device_filter limits an entity to some kinds of
  device when the props alone cannot tell.

Tested against a real LE60-13/GH (firmware 362001062617 V1.27) and against
a new fake controller in tests/fakes/zone.py.
ZoneRole only knew the sub-units of a zone controller. It is now DeviceType
and every device gets one after the info fetch: ac_unit (a standalone
unit), vrf_unit (an indoor unit behind a VRF gateway), zone_controller (the
ducted unit, sub-unit 00 of a zone controller) and zone. Only the two zone
controller types change behaviour, so standalone and VRF units work as
before. The type is in the device diagnostics. Suggested by @p-monteiro in
RobHofmann#549.

The controller device of a zone controller was labelled "VRF gateway". It
is now "Zone controller", taken from the type of a bound sub-unit. With no
bound sub-unit the registry keeps what it has. The lookup moved into a
small helper, so reconcile_vrf_controllers stays simple.
Every fan mode change called set_feature_quiet and set_feature_turbo, also
on a unit without those columns. The state then logged "Property Quiet is
unsupported on this device" at error level on every change. Seen on the
ducted unit of a zone controller, but any unit without Quiet or Turbo did
it. Units that have them work as before.

@RobHofmann RobHofmann left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this, and for taking in the DeviceType idea. I ran the PR in my dev instance against my standalone test unit (encryption v1). The start is clean: same 29-column probe, Device type: ac_unit, and the poll still sends the same 20 columns, without StTem. A fan change still sends Quiet and Tur as before. pytest gives 272 passed. I could not test the zone parts myself, because I have no zone controller.

One blocker and one question.

1. translation_key=UNDEFINED breaks setup when no sub-unit is bound (blocker)

In reconcile_vrf_controllers(), when the controller device already exists and _controller_info() finds no bound sub-unit, model and translation_key stay UNDEFINED and go into async_get_or_create(). That works for model, but translation_key is typed str | None, and Home Assistant does this:

if translation_key:
    name = self._resolve_translated_name(config_entry, translation_key, translation_placeholders)

UNDEFINED is truthy, so the name lookup falls back to the key itself and then calls .format() on it. I ran Home Assistant's own _resolve_translated_name() with UNDEFINED in my dev container:

'vrf_controller' -> 'vrf_controller'
<UndefinedType._singleton: 0> -> RAISES AttributeError 'UndefinedType' object has no attribute 'format'

The call in async_setup_entry() has no try around it. So when a local VRF gateway is offline at Home Assistant start, I expect the whole entry to fail, including standalone units in the same entry, and without a retry because it is not ConfigEntryNotReady. That also hits plain VRF users, not only zone controllers. I did not run that full scenario end to end; the crash itself is reproduced. Mypy flags the same line (helpers.py:475, arg-type), and it is the only new mypy error in the changed files.

A simple fix: only pass model, translation_key and translation_placeholders when the type is known, for example by building the kwargs and leaving those keys out when they are UNDEFINED. A test with an existing controller device and no bound sub-unit would cover it.

2. Zone Switch default in a new setup (question)

async_step_device_options() uses the options of the previous sub-unit of the same controller as defaults (_options_by_controller), and the features default is defaults.get(CONF_FEATURES, valid_features). If the AC unit is configured first, its feature list has no zone, so as far as I can read it, Zone 1 opens with Zone Switch unticked. The other way round, the AC unit could get zone as a default that is not in its options. Did you see this on your controller, or did you tick it by hand? If it is real, filtering the copied defaults against valid_features would solve both cases.

When an existing controller device had no bound sub-unit at start, for
example a VRF gateway that is offline, reconcile_vrf_controllers passed
UNDEFINED as translation_key. Home Assistant types it str | None and checks
it with "if translation_key:", so it tried to format UNDEFINED and the
whole entry failed with AttributeError, with no retry. Found in review.

The model and the name arguments are now left out when the type is not
known, so the registry keeps what it has. A new device still gets the VRF
label. The decision moved into _controller_label.

Reproduced on Home Assistant 2026.10 with a VRF device whose gateway never
answers: before, the entry stayed in setup_error with every entity
unavailable; after, the entry loads and the other devices work.
When the device options step starts from the options of the device of the
same controller set up before, it copied all of them. So each sub-unit's
form showed the name of the device before it, and the features came from
a device of another kind: a zone after the AC unit started with Zone
Switch off, and the AC unit after a zone got a "zone" default it cannot
take. Found in review.

- Only the mode and feature lists are copied now, the same lists the
  copy by model already used. The name stays the device's own.
- The Zone Switch is never copied, because only an always open zone goes
  without it: a zone starts with it on, any other device without it.
- The feature default is filtered against the features the device has.
cleanup_device_connection_issues deleted issues from the issue registry
while it looped over registry.issues. After a device that had a "connection
failed" issue was removed from the config, the next setup deleted that
issue and then failed with "RuntimeError: dictionary changed size during
iteration", so the entry stayed in setup_error. A second stale issue was
never reached.

Found while testing the controller label fix: a VRF device whose gateway
never answers was removed from the YAML again, and the entry failed at the
next start. A manual reload worked, because the issue was already gone.
@Twintop

Twintop commented Oct 10, 2026

Copy link
Copy Markdown
Author

Thanks for testing it on your standalone unit. Both points are fixed, plus a bug I found while testing.

1. translation_key=UNDEFINED (blocker)

Confirmed end to end on Home Assistant 2026.10: with a VRF device whose gateway never answers in the same entry as the zone controller, the entry failed on every start, all its entities were unavailable, and there was no retry.

Fixed in 5bfa6cb. The new _controller_label() leaves out the model and name when no sub-unit is bound and the device already exists, so it keeps its label. A new device gets the VRF gateway label as before. The entry now loads.

Mypy missed it on my side because I had only tested in my Home Assistant instance, not with a local one in the dev environment, so every Home Assistant type was Any. I now run it against a local install with the repo's mypy.ini, and there are no new errors compared with 5.0-dev.

Question: A test for reconcile_vrf_controllers() needs Home Assistant in the test environment, and the suite only imports aiogree today. If you like, I can add pytest-homeassistant-custom-component in a separate PR, with this case as the first test.

2. Zone Switch default (question)

It is real. I had ticked it by hand. My sub-units report no ModelType, so the form falls back to the previous sub-unit's options, and that also copied its name. Fixed in d6abdcd:

  • only the mode lists and features are copied, the same keys as the model path;
  • zone is never copied, so a zone always starts with Zone Switch ticked and other units never get it;
  • the features default is filtered against valid_features.

3. Bug found while testing (already on 5.0-dev)

cleanup_device_connection_issues() deleted repair issues while looping over registry.issues. After a device with a "connection failed" issue was removed, the next start failed with RuntimeError: dictionary changed size during iteration. Fixed in d666fe1 by looping over a copy. Checked on my install: the entry loads, and both the issue and the stale gateway device are removed.

@Twintop
Twintop requested a review from RobHofmann October 10, 2026 12:47
# A zone controller and its sub-units report a name that starts with this.
ZONE_CONTROLLER_NAME_PREFIX = "GR-ZCntrlr"
# The model ids of the sub-units in the sub-device list.
ZONE_CONTROLLER_MID_AC_UNIT = "5000"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we certain that all Gree Zone controllers are 5000/5001 and not only your device?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question, and no, we can't be 100% sure for every model out there. Here's what I found when I dug into it:

  • Those values don't come from the indoor unit at all. The WiFi module builds the sub-device list itself, and 5000, 5001, the zone name and ZCntrlr are all hardcoded in its firmware. I checked v1.21, v1.27 and v1.28 of the ME31-00/C13 firmware and they're the same in all three, so anything with this module should report them the same way.
  • The only other zone controller I could find that talks this protocol is the Braemar LE60 (Seeley), which is the same product with the same ME31-00/C13 module. The other zoning systems people put on Gree units (Airzone, AirTouch, iZone) are third party with their own protocols, so they never show up as a Gree device.
  • At detection time every sub-unit reports the controller's name and model id (GR-ZCntrlr_5000_02_..._EC and 3017), not its own. So detection goes by the name, and only discovery uses the model ids.

To make this easy to extend in case it is actually different per model, 3a806af puts all of it in one table, ZONE_CONTROLLER_MODELS: one entry per model with the name prefix, the MAC suffix of the AC unit and the two model ids. The entry with the longest matching name prefix wins, so the order doesn't matter. Right now there's a general GR-ZCntrlr entry plus a GR-ZCntrlr_5000_ one for the LE60-13/GH as an example (though it is redundant, from a technical sense, and I can drop it if desired). If something like GR-ZCntrlr_3000_... turns up with other values, it just needs its own entry, and anything unknown with the same firmware still falls back to the general one. There's a test that adds a made-up model and checks both discovery and detection.

return self._device_type

@property
def zone_target_temperature(self) -> int | None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this? Can this logic not be wired to the existing target_temperature properties?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, good call, we can definitely refactor this to use the existing piping. I've made this change in 9f11775. Zones now go through target_temperature / set_target_temperature() like everything else, and the zone-only property is gone.

While looking into this we did some more digging and found the controller does °F as well. The wall panel has a temperature unit setting, and the WiFi module has its own columns for it: TemUnit for the unit (not TemUn) and StFahFlg as the zone's Fahrenheit bit. We tested it on the real controller. Switching TemUnit over WiFi flips the whole controller and the panel, and in °F a zone works exactly like SetTem/TemRec on a normal unit, with StTem + 16 as the Celsius part (64 °F = 2/0, 66 °F = 3/0, 67 °F = 3/1, 70 °F = 5/1). One gotcha: if you only send StTem, the controller keeps the old bit (we got 72 °F instead of 71), so both always have to go out together.

Since this is a core part of the zone controller and should work right from the start, we've included it in this PR (d7f57c4) instead of a follow-up. Your suggestion made that easy, because the zone now just reuses the existing °F conversion:

  • On a zone controller, target_temperature_unit uses TemUnit, so the existing Temperature Units select works on the AC unit and switches the whole thing. Zones don't get their own select.
  • The zone number follows the unit (16 to 30 °C or 61 to 86 °F), and HA shows it in your own unit. Number values are now rounded instead of cut, so HA's 69.8 °F for 21 °C goes out as 70.
  • Checked through HA too: switched to °F with the select, set a zone to 21 °C, and it went out as 70 °F (StTem 5, StFahFlg 1).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You bring up a good point with TemUnit, StTem, and StFahFlg, while your tested controller and zones answer these, we don't know if they also apply to other normal ACs and VRFs.
I'm not sure if using them based on it being a zone controller or not is a good idea; maybe we could do prop support verification like for the features.

Do the controllers answer the other "old" props at all, or are they omitted from the status response?
My AC does not answer those at all. So maybe yours don't answer the "old" ones, and we can use whatever the device supports instead of the device type.

Do your devices respond to the Add0.5 prop?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good questions! I checked on the real controller:

  • Old props: no, none of the sub-units answer SetTem, TemRec or TemUn, they're just left out of the reply. The gateway, the AC unit and the zones all answer StTem, StFahFlg and TemUnit instead.
  • Add0.5: no, none of them answer it, and it isn't even in the WiFi module's column table.

So the two sets don't overlap, and your idea works. Since 2f14e87 the columns come from what a device answers, like the features:

  • StTem, StFahFlg and TemUnit are in the default poll, and the first poll drops them on a unit that doesn't answer them, like any other column. So your AC asks for them once, and after that sends the same columns as before. The type-based add_polled() is gone.
  • A device uses TemUn if it answers it, and TemUnit only (I'm assuming; untested as I don't have any other Gree hardware) if it doesn't, so every existing unit works as before.
  • A zone only sends its bit if it answers StFahFlg.

There's one spot where the columns can't tell on their own: the AC unit answers StTem too (always 0), so only a zone uses StTem as its target temperature. The type is still used for that, for the zone controller's mode numbers and turbo, and for which entities a sub-unit gets. Tests cover a normal unit dropping the columns after its first poll, and TemUn winning when a unit answers both.

Rounding moved into the device as well (07f605c, see the comment on number.py).

The name prefix, the MAC suffix of the ducted unit and the two model ids
in the sub-device list were separate constants, so another model with
other values meant changing the code that uses them. Asked in review: are
all zone controllers 5000/5001?

The values come from the WiFi module, not from the units: they are fixed
in its firmware (v1.21, v1.27 and v1.28 checked). Every sub-unit answers
the info request with the name and model id (3017) of the controller, so
detection keeps using the name and only discovery uses the model ids.

ZONE_CONTROLLER_MODELS now has one entry per model. The entry with the
longest matching name prefix is used, so the order does not matter and a
more specific entry wins over a general one. The table has a general
GR-ZCntrlr entry, and a GR-ZCntrlr_5000_ entry for the LE60-13/GH as an
example of a more specific one.
Asked in review. A zone now reads and sets its target through
target_temperature and set_target_temperature, like every other device,
and the zone number uses them. The zone-only properties are gone.

The column stays StTem, in whole degrees Celsius minus 16, clamped to 16
to 30. target_temperature is None until a zone is read, so its number
shows unknown and not 0. A unit gives a value as before.
The wall panel can show Fahrenheit, and the controller has its own
columns for it: TemUnit for the unit (not TemUn) and StFahFlg for the
Fahrenheit bit of a zone. Checked on a LE60-13/GH by switching TemUnit
over WiFi and setting zones on the panel and over WiFi: StTem + 16 and
StFahFlg follow the same table as SetTem and TemRec (64 F is 2 and 0,
66 F is 3 and 0, 67 F is 3 and 1, 70 F is 5 and 1). When only StTem is
sent, the controller keeps the old bit, so both are always sent.

- The sub-units of a zone controller poll TemUnit, zones also StFahFlg
  (ZONE_CONTROLLER_PROPS). Other units send the same columns as before.
- target_temperature_unit uses TemUnit on a zone controller, so the
  Temperature Units select works on its ducted unit and switches the
  whole controller, the wall panel included. Zones get no select.
- The zone number follows the unit: 16 to 30 C or 61 to 86 F. Home
  Assistant shows it in the user's unit.
- A number value is rounded, not cut, before it is sent: Home Assistant
  converts 21 C to 69.8 F, which must go out as 70.
Asked in review: GR-ZCntrlr is a zone controller whatever the model ids
are, so why the table? Detection already went by the name and the MAC
suffix only. The model ids were only used by discovery, to name the
sub-units, and discovery has the name of the controller too: the scan
reply of the gateway.

ZONE_CONTROLLER_MODELS and the model ids are gone. Two constants are left,
ZONE_CONTROLLER_NAME_PREFIX and ZONE_CONTROLLER_AC_UNIT_SUFFIX, and
discovery names the sub-units from the controller name and the MAC
suffix. A test gives the sub-units other model ids and still gets AC unit
and Zone 1 to Zone 4.
Asked in review: StTem, StFahFlg and TemUnit were used because a device
is a zone controller, but other units may answer them too. The sub-units
of the zone controller answer none of SetTem, TemRec, TemUn and Add0.5,
and other units do not answer the new columns. So the columns now come
from what a device answers, like for the features:

- StTem, StFahFlg and TemUnit are in the default poll. The first poll
  drops them on a unit that does not answer them, like any other column,
  so after it every other unit sends the same columns as before. The
  type-based add_polled() is gone.
- A device uses TemUn when it answers it, and TemUnit only without it.
- A zone sends its bit only when it answers StFahFlg.

The type is still used where the columns cannot tell: the mode numbers
and turbo of a zone controller, and which entities a sub-unit gets. The
ducted unit answers StTem too, always with 0, so only a zone uses StTem
as its target temperature.
Asked in review: 25.5 C is valid for units with half degrees, but the
number entity rounded every value to a whole number. The rounding now
happens in set_target_temperature, as it already did for units: Celsius
to the nearest half degree, Fahrenheit to whole degrees. The number
entity passes the value on as it is. The humidity number keeps its int().

A zone in Celsius takes half degrees too: StFahFlg is the half degree,
like TemRec. Checked on a LE60-13/GH: StTem 2 with StFahFlg 1 was still
there after a minute and after using the wall panel, which shows only
whole degrees (18). So a zone now uses the same SetTem/TemRec table in
both units, and its number steps by 0.5 in Celsius and 1 in Fahrenheit.
@p-monteiro

p-monteiro commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Thanks for the changes; it's better now.
Some more changes:

Treat the props like the sensors:

TARGET_TEMPERATURE_1 = "SetTem"
TARGET_TEMPERATURE_2 = "StTem"

The same for the temperature bit and temperature unit

Then call invalidate_missing_property_group() in the same place (in _remove_unsupported_props()) for all 3 groups (target, bit, unit).

This should give a valid _props_to_poll with only one of them. It should also remove StTem from the zone controller, as a value of 0 is invalid for temperature. If 0 is a valid value for temperature because of the MIN_TEMP_C - offset, set the missing_value for it to something like -1. None values are removed in invalidate_missing_properties() before that.

Then, in the device properties, do your logic based on which props are supported.
Usually, (SetTem, TemRec) or (StTem, StFahFlg) are always set at the same time, if they are supported.

In the near future, we will gate half-degrees by a feature option that relies on the device responding to bit or Add0.5, so this keeps it ready for that.

Check what we do for temperature sensors on the device, which matches the case of one property being provided by multiple fields.
Now that you're touching that code, it would be nice to change invalidate_missing_property_group() to return the preferred GreeProp that survived the invalidation, if any, or None.
Then, when we call it, we can save the props in device.py for each:

self._indoor_temp_sensor_prop = invalidate_missing_property_group(...)
self._target_temp_prop = invalidate_missing_property_group(...)
(...)

So that we don't need to check support in the device properties and can directly get/set the correct ones. And adjust the target temperature and temperature units accordingly to respond to the correct prop and logic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants